package cn.zch.soft.util.excel.poi;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.*;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import static org.apache.poi.ss.usermodel.CellType.STRING;
/**
 * @author zchcpy
 * @date 180907
 */
public class PoiExcelUtil {
  private static final Log log = LogFactory.get();
  /**
   * 总行数
   */
  private int totalRows = 0;
  /**
   * 总列数
   */
  private int totalCells = 0;
  /**
   * 错误信息
   */
  private String errorInfo;
  /**
   * 判断导入Excel表内容格式并输出公用方法-20170822-wxb
   */
  private static String getHssTextString(Row row, int colNum) {
    Cell cell = row.getCell(colNum);
    if (null != cell) {
      // 以下是判断数据的类型
      // switch 过时了 (cell.getCellType())...
      switch (cell.getCellTypeEnum()) {
        // case HSSFCell.CELL_TYPE_NUMERIC:
        case NUMERIC:
          // 数字
          if (DateUtil.isCellDateFormatted(cell)) {
            // 如果是date类型则 ，获取该cell的date值
            DateFormat formater = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            return formater.format(DateUtil.getJavaDate(cell.getNumericCellValue()));
          }
          else {
            // 纯数字
            return BigDecimal.valueOf(cell.getNumericCellValue()).toString();
          }
          // case HSSFCell.CELL_TYPE_STRING:
        case STRING:
          // 字符串
          return cell.getStringCellValue();
        // case HSSFCell.CELL_TYPE_BOOLEAN:
        case BOOLEAN:
          // Boolean
          return cell.getBooleanCellValue() + "";
        // case HSSFCell.CELL_TYPE_FORMULA
        case FORMULA:
          // 公式
          return cell.getCellFormula() + "";
        // case HSSFCell.CELL_TYPE_BLANK:
        case BLANK:
          // 空值
          return "";
        // case HSSFCell.CELL_TYPE_ERROR:
        case ERROR:
          // 故障
          return "illeagleStr";
        default:
          return "unkwnType";
      }
    }
    else {
      return "";
    }
  }
  /**
   * 写内容到excel
   */
  public static void writeToExcel(List<List<Object>> valueList, List<Object> headList, String sheetName, String logPath) {
    try {
      // 第一步，创建一个webbook，对应一个Excel文件
      FileOutputStream fileOutputStream;
      try (HSSFWorkbook wb = new HSSFWorkbook()) {
        // 第二步，在webbook中添加一个sheet,对应Excel文件中的sheet
        HSSFSheet sheet;
        if (null == sheetName) {
          sheet = wb.createSheet("sheet1");
        }
        else {
          sheet = wb.createSheet(sheetName);
        }
        // 第三步，在sheet中添加表头第0行,注意老版本poi对Excel的行数列数有限制short
        HSSFRow row = sheet.createRow(0);
        // 第四步，创建单元格，并设置值表头 设置表头居中
        HSSFCellStyle style = wb.createCellStyle();
        // 创建一个居中格式
        // style.setAlignment(HSSFCellStyle.ALIGN_CENTER)
        style.setAlignment(HorizontalAlignment.CENTER_SELECTION);
        if (null == headList) {
          HSSFCell cell = row.createCell(0, STRING);
          cell.setCellValue("列1");
          cell.setCellStyle(style);
          cell = row.createCell(1, STRING);
          cell.setCellValue("列2");
          cell.setCellStyle(style);
          cell = row.createCell(2, STRING);
          cell.setCellValue("列3");
          cell.setCellStyle(style);
          // cell = row.createCell(3, HSSFCell.CELL_TYPE_STRING)
          cell = row.createCell(3, STRING);
          cell.setCellValue("列4");
          cell.setCellStyle(style);
        }
        else {
          HSSFCell cell;
          for (int i = 0; i < headList.size(); i++) {
            cell = row.createCell(i, STRING);
            cell.setCellValue(headList.get(i).toString());
            cell.setCellStyle(style);
          }
        }
        // 第五步，写入实体数据 实际应用中这些数据从数据库得到，
        for (int i = 0; i < valueList.size(); i++) {
          row = sheet.createRow(i + 1);
          // 第四步，创建单元格，并设置值
          List<Object> cellList = valueList.get(i);
          for (int j = 0; j < cellList.size(); j++) {
            row.createCell(j).setCellValue(cellList.get(j).toString());
          }
        }
        // 第六步，将文件存到指定位置
        // FileOutputStream fout = new FileOutputStream("E:/students.xls")
        fileOutputStream = new FileOutputStream(logPath);
        wb.write(fileOutputStream);
      }
      fileOutputStream.close();
    }
    catch (Exception e) {
      e.printStackTrace();
    }
  }
  /**
   * writeExcelAppend
   */
  public static void writeExcelAppend(String path, int sheetAddr, List<List<Object>> contentList) {
    try {
      // String path = "E://登录及命令监控.xlsx"; 获取d://test.xls
      try (FileInputStream fs = new FileInputStream(path)) {
        if (isExcel2003(path)) {
          // 使用POI提供的方法得到excel的信息
          POIFSFileSystem ps = new POIFSFileSystem(fs);
          HSSFWorkbook wb = new HSSFWorkbook(ps);
          // 获取到工作表，因为一个excel可能有多个工作表
          HSSFSheet sheet = wb.getSheetAt(sheetAddr);
          // 获取第一行（excel中的行默认从0开始，所以这就是为什么，一个excel必须有字段列头），即，字段列头，便于赋值
          HSSFRow row;
          // 分别得到最后一行的行号，和一条记录的最后一个单元格
          // 向d://test.xls中写数据
          try (FileOutputStream out = new FileOutputStream(path)) {
            //在现有行号后追加数据
            for (int i = 0; i < contentList.size(); i++) {
              List<Object> cellList = contentList.get(i);
              row = sheet.createRow((short) (sheet.getLastRowNum() + (i + 1)));
              for (int j = 0; j < cellList.size(); j++) {
                row.createCell(j).setCellValue(cellList.get(j).toString());
              }
            }
            out.flush();
            wb.write(out);
          }
        }
        else if (isExcel2007(path)) {
          XSSFWorkbook wb = new XSSFWorkbook(fs);
          // 获取到工作表，因为一个excel可能有多个工作表
          XSSFSheet sheet = wb.getSheetAt(sheetAddr);
          // 获取第一行（excel中的行默认从0开始，所以这就是为什么，一个excel必须有字段列头），即，字段列头，便于赋值
          //= sheet.getRow(0)
          XSSFRow row;
          // 分别得到最后一行的行号，和一条记录的最后一个单元格
          // row.getLastCellNum())
          // 向d://test.xls中写数据
          try (FileOutputStream out = new FileOutputStream(path)) {
            for (int i = 0; i < contentList.size(); i++) {
              List<Object> cellList = contentList.get(i);
              row = sheet.createRow((short) (sheet.getLastRowNum() + (i + 1)));
              for (int j = 0; j < cellList.size(); j++) {
                row.createCell(j).setCellValue(cellList.get(j).toString());
              }
            }
            out.flush();
            wb.write(out);
          }
        }
      }
    }
    catch (IOException e) {
      e.printStackTrace();
    }
  }
  /**
   * 180907 获取超充值消费人员
   */
  private static void getOverConsumeRecordUid() {
    PoiExcelUtil poi = new PoiExcelUtil();
    // Workbook wb = poi.read
    List<List<Object>> list = poi.read("E:/charge.xlsx");
    List<List<Object>> list2 = poi.read("E:/consumere.xlsx");
    if (list == null || list2 == null) {
      return;
    }
    for (List<Object> aList : list) {
      // // print("A:" + aList.get(0) + "|" + aList.get(1))
      for (List<Object> bList : list2) {
        if (aList.get(0).toString().equals(bList.get(0))) {
          // print("B:" + bList.get(0) + "|" + bList.get(1))
          int cha = new BigDecimal(aList.get(1).toString()).intValue();
          int con = new BigDecimal(bList.get(1).toString()).intValue();
          int sub = (cha - con);
          // print("C:" + sub)
          if (sub < 0) {
            // print("D:" + sub + " " + aList + " " + bList)
            log.warn("D:" + sub + " " + aList + " " + bList);
          }
        }
      }
      // print("--------------------------")
    }
    // WriteExcelAppend("E://登录及命令监控.xlsx", 1, listWrite)
  }
  /**
   * 描述：是否是2003的excel，返回true是2003
   * 时间：2014-08-29 下午16:29:11
   * 参数：param filePath　文件完整路径
   * 参数：return
   * 返回值：boolean
   */
  public static boolean isExcel2003(String filePath) {
    return filePath.matches("^.+\\.(?i)(xls)$");
  }
  /**
   * 描述：是否是2007的excel，返回true是2007
   * 时间：2014-08-29 下午16:28:20
   * 参数：param filePath　文件完整路径
   * 参数：return
   * 返回值：boolean
   */
  public static boolean isExcel2007(String filePath) {
    return filePath.matches("^.+\\.(?i)(xlsx)$");
  }
  /**
   * 得到总行数
   */
  public int getTotalRows() {
    return totalRows;
  }
  /**
   * 得到总列数
   * 2014-08-29 下午16:27:15
   */
  private int getTotalCells() {
    return totalCells;
  }
  /**
   * 得到错误信息
   * 2014-08-29 下午16:27:15
   */
  public String getErrorInfo() {
    return errorInfo;
  }
  /**
   * 验证excel文件
   * 2014-08-29 下午16:27:15
   *
   * @param filePath 文件完整路径
   */
  private boolean validateExcel(String filePath) {
    /* 检查文件名是否为空或者是否是Excel格式的文件 */
    boolean isNotExcel = !(isExcel2003(filePath) || isExcel2007(filePath));
    if (isNotExcel) {
      errorInfo = "文件名不是excel格式";
      return false;
    }
    /* 检查文件是否存在 */
    File file = new File(filePath);
    if (!file.exists()) {
      errorInfo = "文件不存在";
      return false;
    }
    return true;
  }
  /**
   * 根据文件名读取excel文件
   * 2014-08-29 下午16:27:15
   *
   * @param filePath 文件完整路径
   */
  private List<List<Object>> read(String filePath) {
    List<List<Object>> dataLst;
    try (InputStream is = new FileInputStream(filePath)) {
      /* 验证文件是否合法 */
      if (!validateExcel(filePath)) {
        return Collections.emptyList();
      }
      /* 判断文件的类型，是2003还是2007 */
      boolean isExcel2003 = !isExcel2007(filePath);
      /* 调用本类提供的根据流读取的方法 */
      dataLst = read(is, isExcel2003);
      // is.close()
      return dataLst;
    }
    catch (Exception ex) {
      ex.printStackTrace();
      return Collections.emptyList();
    }

    /* 返回最后读取的结果 */
  }
  /**
   * 根据文件名读取excel文件
   * 2014-08-29 下午16:27:15
   *
   * @param file 文件
   */
  public List<List<Object>> read(File file) {
    List<List<Object>> dataLst = new ArrayList<>();
    try (InputStream is = new FileInputStream(file)) {

      /* 判断文件的类型，是2003还是2007 */
      boolean isExcel2003 = !isExcel2007(file.getName());
      dataLst = read(is, isExcel2003);
    }
    catch (Exception ex) {
      ex.printStackTrace();
    }
    /* 返回最后读取的结果 */
    return dataLst;
  }
  /**
   * 根据流读取Excel文件 @时间：2014-08-29 下午16:40:15
   */
  private List<List<Object>> read(InputStream inputStream, boolean isExcel2003) {
    List<List<Object>> dataLst = null;
    try {
      /* 根据版本选择创建Workbook的方式 */
      Workbook wb;
      if (isExcel2003) {
        wb = new HSSFWorkbook(inputStream);
      }
      else {
        wb = new XSSFWorkbook(inputStream);
      }
      dataLst = read(wb);
    }
    catch (IOException e) {
      e.printStackTrace();
    }
    return dataLst;
  }
  /**
   * 读取数据 @时间：2014-08-29 下午16:50:15
   */
  private List<List<Object>> read(Workbook wb) {
    List<List<Object>> dataLst = new ArrayList<>();
    /* 得到第一个shell */
    Sheet sheet = wb.getSheetAt(0);
    /* 得到Excel的行数 */
    this.totalRows = sheet.getPhysicalNumberOfRows();
    /* 得到Excel的列数 */
    if (this.totalRows >= 1 && sheet.getRow(0) != null) {
      this.totalCells = sheet.getRow(0).getPhysicalNumberOfCells();
    }
    /* 循环Excel的行 */
    for (int r = 0; r < this.totalRows; r++) {
      Row row = sheet.getRow(r);
      if (row == null) {
        continue;
      }
      List<Object> rowLst = new ArrayList<>();
      /* 循环Excel的列 */
      for (int c = 0; c < this.getTotalCells(); c++) {
        String cellValue = getHssTextString(row, c);
        rowLst.add(cellValue);
      }
      /* 保存第r行的第c列 */
      dataLst.add(rowLst);
    }
    return dataLst;
  }
  public List<List<Object>> readLog(InputStream is, boolean isExcel2003) {
    List<List<Object>> dataLst = null;
    try {
      /* 根据版本选择创建Workbook的方式 */
      Workbook wb;
      if (isExcel2003) {
        wb = new HSSFWorkbook(is);
      }
      else {
        wb = new XSSFWorkbook(is);
      }
      dataLst = readLogWithWorkbook(wb);
    }
    catch (IOException e) {
      e.printStackTrace();
    }
    return dataLst;
  }
  private List<List<Object>> readLogWithWorkbook(Workbook wb) {
    List<List<Object>> dataLst = new ArrayList<>();
    /* 得到第一个shell */
    Sheet sheet = wb.getSheetAt(0);
    /* 得到Excel的行数 */
    this.totalRows = sheet.getPhysicalNumberOfRows();
    /* 得到Excel的列数 */
    if (this.totalRows >= 1 && sheet.getRow(0) != null) {
      this.totalCells = sheet.getRow(0).getPhysicalNumberOfCells();
    }
    /* 循环Excel的行 */
    for (int r = 0; r < this.totalRows; r++) {
      Row row = sheet.getRow(r);
      if (row == null) {
        continue;
      }
      List<Object> rowLst = new ArrayList<>();
      /* 循环Excel的列 */
      for (int c = 0; c < this.getTotalCells(); c++) {
        // String cellValue = getHSSTextString(row, c)
        Cell cell = row.getCell(c);
        if (cell == null) {
          continue;
        }
        // cell.setCellType(Cell.CELL_TYPE_STRING)
        cell.setCellType(STRING);
        String cellValue = cell.getStringCellValue();
        rowLst.add(cellValue);
      }
      /* 保存第r行的第c列 */
      dataLst.add(rowLst);
    }
    return dataLst;
  }
}
